package com.duang.framework.config;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.duang.framework.core.Resource;
import com.duang.framework.dao.BaseDao;
import com.duang.framework.dao.DaoSingleFactory;
import com.duang.framework.service.BaseService;
import com.duang.framework.util.IntrospectionUtils;

/**
 * @QQ 1003031335
 * @author zgq
 * 
 */
public class BeanContainer {

	private Map<String, Object> beanMap = new HashMap<String, Object>();
	private List<Object> beanContainer = new ArrayList<Object>();
	private List<InjectedBean> beanList = new ArrayList<InjectedBean>();

	public static BeanContainer handleServiceBeanInjection(
			List<ServiceComponent> serviceComponentList) {
		BeanContainer beanContainer = new BeanContainer();
		for (ServiceComponent serviceComponent : serviceComponentList) {
			Class<?> daoClazz = serviceComponent.getDaoImplClazz();
			Class<?> serviceClazz = serviceComponent.getServiceImplClazz();
			// ModelSource source = daoClazz.getAnnotation(ModelSource.class);
			// Model model = Model.getModel(source.value());
			try {
				if (SystemConfiguration.isDedug())
					System.out.println("start register--->"
							+ daoClazz.getName());
				Constructor<?> constructor = daoClazz.getConstructor();
				BaseDao<?> dao = (BaseDao<?>) constructor.newInstance();
				DaoSingleFactory
						.registerDao(serviceComponent.getDaoName(), dao);
				beanContainer.findAndAddBeanNeedToBeInjected(dao);
				Constructor<?> serviceConstructor;
				BaseService<?> service;
				try {
					serviceConstructor = serviceClazz
							.getConstructor(serviceComponent.getDaoInterface());
					service = (BaseService<?>) serviceConstructor.newInstance();
				} catch (NoSuchMethodException | SecurityException e1) {
					serviceConstructor = serviceClazz.getConstructor();
					service = (BaseService<?>) serviceConstructor
							.newInstance(new Object[] { dao });
				}
				beanContainer.findAndAddBeanNeedToBeInjected(service, dao);
				beanContainer.registerBean(serviceComponent.getServiceName(),
						service);
				beanContainer.registerBean(serviceComponent.getDaoName(), dao);
			} catch (Exception e) {
				throw new ConfigureApplicationException(e);
			}
		}
		beanContainer.performReallyInjection();
		return beanContainer;
	}

	public void addBeanNeedToBeInjected(Field field, Object obj, String name) {
		beanList.add(new InjectedBean(field, obj, name));

	}

	public class InjectedBean {

		public InjectedBean(Field field2, Object obj2, String name2) {
			this.field = field2;
			this.obj = obj2;
			this.name = name2;
		}

		Field field;
		Object obj;
		String name;

		@Override
		public String toString() {
			return "InjectedBean [field=" + field + ", obj=" + obj + ", name="
					+ name + "]";
		}

	}

	public void findAndAddBeanNeedToBeInjected(Object component, Object... dao) {
		List<Field> fieldList = IntrospectionUtils.getFields(
				component.getClass(), true);
		for (Field field : fieldList) {
			if (SystemConfiguration.isDedug())
				System.out.println("field--->" + field);

			if (field.isAnnotationPresent(Resource.class)) {

				Resource name = field.getAnnotation(Resource.class);
				if (name.value().equals("baseDao")) {
					if (dao.length >= 1)
						injectValueToField(field, component, dao[0]);
				} else {
					addBeanNeedToBeInjected(field, component, name.value());
				}
			}
		}

	}

	public void registerBean(String name, Object obj) {
		beanMap.put(name, obj);
		beanContainer.add(obj);
	}

	void injectValueToField(Field field, Object obj, Object value)
			throws InjectingBeanException {
		field.setAccessible(true);
		try {
			System.out.println(obj + "" + value);
			field.set(obj, value);

		} catch (Exception e) {
			throw new InjectingBeanException(e);
		}
	}

	public Object findBean(String name) {
		return beanMap.get(name);
	}

	public void performReallyInjection() throws InjectingBeanException {

		for (InjectedBean bean : beanList) {
			System.out.println("inject ......" + bean);
			if (bean.name == null || bean.name.equals("")) {
				// find type
				int matchedNum = 0;
				Object matchedObj = null;
				for (Object obj : beanContainer) {
					if (bean.field.getType().isAssignableFrom(obj.getClass())) {
						matchedNum++;
						matchedObj = obj;
					}
				}
				if (matchedNum == 1) {
					if (matchedObj == null) {
						throw new InjectingBeanException(
								"inject bean must be not null");
					} else {
						System.out.println("inject ......");
						injectValueToField(bean.field, bean.obj, matchedObj);
					}
				} else if (matchedNum == 0) {
					throw new InjectingBeanException("not found matched bean");
				} else {
					throw new InjectingBeanException(
							"found more than one matched bean");
				}
			} else {
				Object matchedObj = beanMap.get(bean.name);
				if (matchedObj == null) {
					throw new InjectingBeanException("matched a null value--"
							+ bean.name);
				}
				System.out.println("inject obj......" + matchedObj);
				injectValueToField(bean.field, bean.obj, matchedObj);

			}

		}

	}

}
